SAMLite
Developer Documentation
Stop Motion Animation Software
02.03.2006

Please also see comments in source...

Library Requirements:
	- Quicktime 5.0
	- OpenGL 1.2

Developer Tools:
	- Mac OS X 10.3
		- Xcode 2.0
	- Windows XP		???
		- Visual C++	???
		


###############################################################################
### 2006.03.09 ################################################################
###############################################################################

Alrighty, I've stripped out a bit of my "test" stuff, to see if we can get a solid common code base to work with.  Attached is the source.  All it should do is get an OpenGL window up, draw a black background, and if a camera is plugged in, draw the live picture at some spot.  In reality, it will also be testing the compatibility of some Quicktime and OpenGL convenience functions.

A brief rundown.  All files ending in _OSX.* are mac specific.  Don't bother trying to compile them.  If you need to add windows specific files, suffix them with _WIN.*.

Platform.*
	Right now, to determine which platform we are compiling on, we can set one of two flags, SAM_OSX and SAM_WIN.  Before you get started, be sure to change SAM_WIN to 1 and SAM_OSX to 0 in Platform.h.  If you need to make platform specific changes in some of the common files, e.g. change includes around, wrap them like so:

#if SAM_OSX
	// do mac stuff here
#elif SAM_WIN
	// do win stuff dere
#endif

One thing you need to add in Platform.c is some code to write a win version of SAM_resourceFilePath().  There are going to be certain resources we need to find (e.g. images, config files, localization stuff), and need a consistent place to put them.  On the mac, everything is contained within the application "bundle".  Perhaps there is something similar in windows.  SAM_resourceFilePath takes 2 arguments (sorry this isn't documented in the source).  name_in is the name of the file you wish to find the the resources directory, path_out is a buffer of size at least 1024 that is filled when the function returns of the full path of the file, appropriate for use in something like fopen().

SAMCamera.*
	Wrappers for the Quicktime sequence grabber.  Documented a bit already in the header file.  EVERYTHING in these files should be cross platform.  If you have to make windows-specific changes, wrap them in the #if SAM_WIN stuff (described above).

SAMImage.*
	Wrappers for Quicktime and OpenGL image handling.  Right now only offers convenience functions to get an image file into a GWorld and then into an OpenGL texture.

SAMDocument.*
	This is where the magic is going to happen.  This is going to expose a very clean interface for the platform specific stuff in mess with.  Right now there are only 4 functions:
	extern void SAMDocument_create(SAMDocumentRef doc);
	extern void SAMDocument_destroy(SAMDocumentRef doc);
	extern void SAMDocument_draw(SAMDocumentRef doc);
	extern void SAMDocument_tick(SAMDocumentRef doc);

_create() initializes a "document".  This document is completely self contained.  it will handle setting up the cameras, taking snapshots, recording audio (eventually).  We just need to call some functions occasionally.  call this function AFTER you have set up everything else.  It REQUIRES there to be an OpenGL context already set up AND active.

_destroy() is pretty obvious.  call this before you quit... and before you tear down any OpenGL stuff.  this may call opengl functions, e.g. to free up textures.

_draw() this will take care of doing all the UI drawing, but you need to do a little bit of work before you call it.  for example, on the mac side, the code for drawing looks like this, the comments may help:

	[[self openGLContext] makeCurrentContext];	 // need to make my opengl context active first
	glClearColor(0.0f,0.0f,0.0f,0.0f); // bla
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the screen
	glViewport(0, 0, [self bounds].size.width, [self bounds].size.height); // you should call this first, depending on how big the window you set up is.
	SAMDocument_draw(&doc); // "doc" here is a SAMDocument that I set up prior with SAMDocument_create
	glFlush(); // flush
	[[self openGLContext] flushBuffer]; // mac specific flush

_tick() needs to be called pretty often.  this will give some time to the sequence grabber, and update the internal preview texture.  i call it every 1/7th of a second right now.  I do this with a timer that calls a method in my main event loop. DON'T try to do this in a separate thread.  Both Quicktime and OpenGL hate threads.  I don't know too much windows programming, but you can probably do something similar with a WM_TIMER message or something.  after you call this, be sure to trigger a redraw of the window.

eventually, there will be functions like SAMDocument_mouseDown / up / dragged... which we will pass our own special ways from the platform specific files.  but we should get this working first.

so, getting started...
1. change the stuff in Platform.h
2. add you windows stuff in main_WIN.c
it should look something like

main {
	SAMDocument doc;
	// set up window
	// set up opengl
	SAMDocument_create(&doc);
	// set up 1/7th second timer
	main_event_loop {
		handle_timer {
			SAMDocument_tick(&doc);
			// trigger redraw of window
		}
	}
	SAMDocument_destroy(&doc); // call when you are ready to quit
	// destroy opengl, and the window
}

and call SAMDocument_draw wherever you do your opengl drawing.

3. let me know what breaks.  it might be my fault.

Have fun playing.  sharing source is going to be a bit awkward without something like subversion set up... but it shouldn't be too bad if we only touch our own stuff.  i guess if you have to make any changes to the common stuff just mark it somehow, so if I work on the same file we can merge them later.



###############################################################################
### 2006.04.27 ################################################################
###############################################################################

Alrighty, I put together our stuff.  The problem with the small image size had to do with the fact that you re-ordered the calls in Camera_create.  It didn't properly set the rect for the sequence grabber to draw into, but I fixed it... now it's just forcing everything to 640x480 like chris wanted.

Attached is the new base that we should work from.  The interface code is all there.  There are 3 new files that you need to take care of, gui_normal.tif, gui_hover.tif, and gui_active.tif.  These should go into the resources directory that you defined... the app will look for them there when it starts up.  They define what the interface looks like in the normal, mouse-over, and mouse-clicked states.  Right now the rects that should change are hard-coded, but we could throw that into a config file later.

You need to handle a bit more event stuff too.  SAMDocument exposes a few new functions that it expects you to call when there are mouse events:

extern int SAMDocument_mouseUp(SAMDocumentRef doc, SAMPoint point);
extern int SAMDocument_mouseDown(SAMDocumentRef doc, SAMPoint point);
extern int SAMDocument_mouseMove(SAMDocumentRef doc, SAMPoint point);
extern int SAMDocument_mouseDrag(SAMDocumentRef doc, SAMPoint point);

when the window gets a mouse event, you need to first create a SAMPoint with the mouse location, normalized in such a way that <0.0, 0.0> is the lower-left, and <1.0, 1.0> is the upper-right.

The SAMDocument takes care of the rest... oh, and trigger a re-draw when a mouse event occurs, just for good measure.

So, if you get this up and running, it should start up, you should see the some sexy temp interface, click on some buttons, and when you click on "snap", a frame should hop down into the timeline.



###############################################################################
### 2006.04.07 ################################################################
###############################################################################

What's needed on the Windows side:

Right after you call SAMDocument_create, you need to call:

extern void SAMDocument_setRequestProc(SAMDocumentRef doc, 
	SAMDocumentReqProc proc,
	void *cookie);

Pass the &doc, the address of a callback function (explained below), and a cookie (if you need to).  this cookie will be passed back to your callback function.

Your callback function should have the following signature:

void my_callback_func(
	SAMDocumentRef doc, 
	int request, 
	void *data,
	void *cookie);

when it is called, 'doc' will be the doc you should work with, 'cookie' will be what you passed to _setRequestProc, and 'request' will be one of the following:

	kSAMRequestOpen
	kSAMRequestSave
	kSAMRequestExport
	kSAMOpenFileInViewer
	
when the user clicks on the open, save, or export buttons, your callback will be called, and you need to do a little work.  in these cases, the 'void* data' will actually be a pointer to a SAMRequestPath, which looks like this:

	typedef struct {
		const char *title;
		const char *prompt;
	} SAMRequestPath;

you need to bring up a panel for opening, saving, or exporting.  'title' should be the title of the panel... it will probably just be set to 'Export Your Movie' or something.  you should have 2 buttons, one set to 'Cancel', and the other set to the text of the 'prompt' field.

If the user successfully uses the panel, either to navigate to an existing file the the case of opening, or enters the desired name of the file, along with selecting the destination directory (in the case of saving or exporting), you must then call the appropriate function:

extern void SAMDocument_export(SAMDocumentRef doc, const char *path);
extern void SAMDocument_save(SAMDocumentRef doc, const char *path);
extern void SAMDocument_open(SAMDocumentRef doc, const char *path);

where 'path' is the full, absolute path of whatever they wanted.

right now SAMDocument_save and SAMDocument_open don't do anything, but SAMDocument_export should be fully functional.

On the other hand, if your callback function gets a 'request' of type kSAMOpenFileInViewer, something different needs to happen.  the 'data' supplied to your function in this case is actually a pointer to a SAMRequestOpenFileInViewer type (which looks like this:)

	typedef struct {
		const char *path;
	} SAMRequestOpenFileInViewer;

where 'path' is the absolute path of the file you need to magically open in the default viewer (most likely it will be a .mov you need to open in Quicktime).  (this will be called after exporting, so users can preview their movie immediately)

so the skeleton of your callback should be something like this:

static void handle_request(
	SAMDocumentRef doc, int request, void *data, void *cookie)
{
	switch(request)
	{
		case kSAMRequestOpen:
		{
			SAMRequestPath *d = (SAMRequestPath*)data;
			// 'd->title' is the title
			// 'd->prompt' is the button text
			// spawn an open panel.  when it is done, call
			// SAMDocument_open(doc, PATH); 
			// where PATH is whatever path you got from the panel
		}
		case kSAMRequestExport:
		{
			SAMRequestPath *d = (SAMRequestPath*)data;
			// 'd->title' is the title
			// 'd->prompt' is the button text
			// spawn an export (save) panel.  when it is done, call
			// SAMDocument_export(doc, PATH); 
			// where PATH is whatever path you got from the panel
		}
		case kSAMRequestSave:
		{
			SAMRequestPath *d = (SAMRequestPath*)data;
			// 'd->title' is the title
			// 'd->prompt' is the button text
			// spawn an save panel.  when it is done, call
			// SAMDocument_save(doc, PATH); 
			// where PATH is whatever path you got from the panel
		}
		case kSAMOpenFileInViewer:
		{
			SAMRequestOpenFileInViewer *d = (SAMRequestOpenFileInViewer*)data;
			// 'd->path' is the filename you need to tell windows to open
			// in the default viewer (probably Quicktime)
		}
		default:
			break;
	}
}

and just be sure to pass &handle_request to SAMDocument_setRequestProc.

That's it for the callback... but there's more!!!

There is new exporting code in SAMMovie.c, which SHOULD be cross platform, but you will undoubtedly find some issues.  The new stuff in SAMDocument.c should be all fine unless I did something incredibly stupid.  I optimized the UI drawing code so that it handles internal dirty rects and only draws what is needed at the moment.  What this means though is that the doc needs to be notified when EVERYTHING needs to be redrawn (e.g. in the case of resizing the window).  I took the liberty of adding the call to your ReSizeGLScene, but you may need to add it in other places.

All the UI code has become quite crufty since I added the optimized drawing stuff.  I will clean it up for the next revision.

I included 2 new resource files, gui_normal.jpg and gui_hover.jpg.  This should be all you need now, you can toss those old tifs.



###############################################################################
### 2006.05.07 ################################################################
###############################################################################

Created subversion repository.

some new stuff in on svn.  mainly the event functions need to be changed.  the old functions are still there so everything should still compile, but everything should switch over to the new ones. for example, 

extern int SAMDocument_mouseUp(SAMDocumentRef doc, SAMPoint point); // deprecated

is now:

extern int SAMDocument_mouseUpM(SAMDocumentRef doc, SAMPoint point, SAMModifierFlags flags);

(note the "M").

SAMModifierFlags is defined as

typedef enum {
	kSAMNoFlag			= 0,
	kSAMShiftFlag 		= (1 << 0),
	kSAMControlFlag 	= (1 << 1),
	kSAMAltFlag 		= (1 << 2),
	kSAMCommandFlag 	= (1 << 3), // Mac OS X only
} SAMModifierFlags;

when you call the new event functions, you need to build up a SAMModifierFlags with the modifiers that are currently pressed. on the OS X side I just made a quick converter function:

static SAMModifierFlags SAMModifierFlagsFromNSEvent(NSEvent *event)
{
	SAMModifierFlags flags = kSAMNoFlag;
	unsigned int modifierFlags = [event modifierFlags];
	if(modifierFlags & NSShiftKeyMask) flags |= kSAMShiftFlag;
	if(modifierFlags & NSControlKeyMask) flags |= kSAMControlFlag;
	if(modifierFlags & NSAlternateKeyMask) flags |= kSAMAltFlag;
	if(modifierFlags & NSCommandKeyMask) flags |= kSAMCommandFlag;
	return flags;
}

one more thing... I renamed dirent.c/h from the repository to dirent_WIN.* because for some reason in the OS X code my #includes were picking up your dirent files (but my include paths didn't SEEM like they should)... anyway, after about 2 hours and some REALLY weird compile errors I figured out that was the problem.   this seemed like the easiest fix, but you may need to make some more changes on your end.
